﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using Model;

public class Client
{
    public const string DEFAULT_EMLAAEI = Server.DEFAULT_EMLAAEI;
    public const string DEFAULT_TRANSLATION = Server.DEFAULT_TRANSLATION;
    public const string DEFAULT_TRANSLITERATION = Server.DEFAULT_TRANSLITERATION;
    public const string DEFAULT_RECITATION = Server.DEFAULT_RECITATION;

    private string m_machine = "";
    public string Machine
    {
        get { return m_machine; }
    }
    private string m_username = "";
    public string Username
    {
        get { return m_username; }
    }
    private string m_password = "";
    public string Password
    {
        get { return m_password; }
        set
        {
            if (m_password != value)
            {
                m_password = value;
            }
        }
    }

    private Book book = null;
    public Book Book
    {
        get { return book; }
    }
    public Client()
    {
        book = Server.BuildSimplifiedBook();
    }

    public string GetTranslationKey(string translation)
    {
        return Server.GetTranslationKey(book, translation);
    }
    public void LoadTranslation(string translation)
    {
        Server.LoadTranslation(book, translation);
    }
    public void UnloadTranslation(string translation)
    {
        Server.UnloadTranslation(book, translation);
    }

    private Selection m_selection = null;
    public Selection Selection
    {
        get
        {
            if (book != null)
            {
                return m_selection;
            }
            return null;
        }
        set
        {
            if (book != null)
            {
                m_selection = value;
            }
        }
    }
    private void InitializeSelection()
    {
        if (book != null)
        {
            if (m_selection != null)
            {
                m_selection = new Selection(book, SelectionScope.Chapter, new List<int>() { 0 });
            }
        }
    }
    private bool m_is_translation = false;
    public bool IsTranslation
    {
        set { m_is_translation = value; }
        get { return m_is_translation; }
    }
    private FindScope m_find_scope = FindScope.Book;
    public FindScope FindScope
    {
        set { m_find_scope = value; }
        get { return m_find_scope; }
    }
    private List<Verse> m_found_verses = null;
    public List<Verse> FoundVerses
    {
        set { m_found_verses = value; }
        get { return m_found_verses; }
    }
    private List<Phrase> m_found_phrases = null;
    public List<Phrase> FoundPhrases
    {
        set { m_found_phrases = value; }
        get
        {
            return m_found_phrases;
        }
    }
    private List<Word> m_found_words = null;
    public List<Word> FoundWords
    {
        set { m_found_words = value; }
        get { return m_found_words; }
    }
    private List<List<Word>> m_found_word_ranges = null;
    public List<List<Word>> FoundWordRanges
    {
        set { m_found_word_ranges = value; }
        get { return m_found_word_ranges; }
    }
    private List<List<Verse>> m_found_verse_ranges = null;
    public List<List<Verse>> FoundVerseRanges
    {
        set { m_found_verse_ranges = value; }
        get { return m_found_verse_ranges; }
    }
    private List<Chapter> m_found_chapters = null;
    public List<Chapter> FoundChapters
    {
        set { m_found_chapters = value; }
        get { return m_found_chapters; }
    }

    // helper methods with GetSourceVerses (not entire book verses)
    public Dictionary<string, int> GetWordsWith(string text, bool anywhere_in_word)
    {
        Dictionary<string, int> result = new Dictionary<string, int>();
        List<Verse> source = Server.GetSourceVerses(book, m_find_scope, m_selection, m_found_verses);
        if (book != null)
        {
            result = book.GetWordsWith(source, text, anywhere_in_word);
        }
        return result;
    }
    public Dictionary<string, int> GetCurrentWords(string text, bool anywhere_in_word)
    {
        Dictionary<string, int> result = new Dictionary<string, int>();
        List<Verse> source = Server.GetSourceVerses(book, m_find_scope, m_selection, m_found_verses);
        if (book != null)
        {
            result = book.GetCurrentWords(source, text, anywhere_in_word);
        }
        return result;
    }
    public Dictionary<string, int> GetNextWords(string text, bool anywhere_in_word)
    {
        Dictionary<string, int> result = new Dictionary<string, int>();
        List<Verse> source = Server.GetSourceVerses(book, m_find_scope, m_selection, m_found_verses);
        if (book != null)
        {
            result = book.GetNextWords(source, text, anywhere_in_word);
        }
        return result;
    }

    public List<Phrase> FindPhrases(string text, FindByTextLanguageType language_type, string translation, FindByTextLocation text_location, bool case_sensitive, FindByTextWordness wordness, int multiplicity, bool anywhere_in_word)
    {
        m_found_phrases = Server.FindPhrases(book, m_find_scope, m_selection, m_found_verses, text, language_type, translation, text_location, case_sensitive, wordness, multiplicity, anywhere_in_word);

        if (m_found_phrases != null)
        {
            m_found_verses = new List<Verse>();
            foreach (Phrase phrase in m_found_phrases)
            {
                if (!m_found_verses.Contains(phrase.Verse))
                {
                    m_found_verses.Add(phrase.Verse);
                }
            }

            IsTranslation = (language_type == FindByTextLanguageType.Translation);
        }
        return m_found_phrases;
    }
    public List<Phrase> FindPhrases(string root, int multiplicity)
    {
        m_found_phrases = Server.FindPhrases(book, m_find_scope, m_selection, m_found_verses, root, multiplicity);
        if (m_found_phrases != null)
        {
            m_found_verses = new List<Verse>();
            foreach (Phrase phrase in m_found_phrases)
            {
                if (!m_found_verses.Contains(phrase.Verse))
                {
                    m_found_verses.Add(phrase.Verse);
                }
            }
        }
        return m_found_phrases;
    }
    public List<Phrase> FindPhrases(string text, double similarity_percentage)
    {
        m_found_phrases = Server.FindPhrases(book, m_find_scope, m_selection, m_found_verses, text, similarity_percentage);
        if (m_found_phrases != null)
        {
            m_found_verses = new List<Verse>();
            foreach (Phrase phrase in m_found_phrases)
            {
                if (!m_found_verses.Contains(phrase.Verse))
                {
                    m_found_verses.Add(phrase.Verse);
                }
            }
        }
        return m_found_phrases;
    }

    public List<Verse> FindVerses(Verse verse, FindBySimilarityMethod similarity_method, double similarity_percentage)
    {
        m_found_verses = Server.FindVerses(book, m_find_scope, m_selection, m_found_verses, verse, similarity_method, similarity_percentage);
        return m_found_verses;
    }
    public List<List<Verse>> FindVerseRanges(FindBySimilarityMethod similarity_method, double similarity_percentage)
    {
        m_found_verse_ranges = Server.FindVerseRanges(book, m_find_scope, m_selection, m_found_verses, similarity_method, similarity_percentage);
        if (m_found_verse_ranges != null)
        {
            m_found_verses = new List<Verse>();
            foreach (List<Verse> verse_range in m_found_verse_ranges)
            {
                m_found_verses.AddRange(verse_range);
            }
        }
        return m_found_verse_ranges;
    }

    public List<Verse> FindVerses(FindByProstrationType prostration_type)
    {
        m_found_verses = Server.FindVerses(book, m_find_scope, m_selection, m_found_verses, prostration_type);
        return m_found_verses;
    }

    public List<Chapter> FindChapters(FindByRevelationPlace revelation_place)
    {
        m_found_chapters = Server.FindChapters(book, m_find_scope, m_selection, m_found_verses, revelation_place);
        return m_found_chapters;
    }

    private string m_history_directory = "History";
    private List<object> m_history_items = new List<object>();
    public List<object> HistoryItems
    {
        get { return m_history_items; }
    }
    private int m_history_item_index = -1;
    public int HistoryItemIndex
    {
        get { return m_history_item_index; }
    }
    public object CurrentHistoryItem
    {
        get
        {
            if (m_history_items != null)
            {
                if ((m_history_item_index >= 0) && (m_history_item_index < m_history_items.Count))
                {
                    return m_history_items[m_history_item_index];
                }
            }
            return null;
        }
    }
    public void AddHistoryItem(object item)
    {
        if (m_history_items != null)
        {
            m_history_items.Add(item);
            m_history_item_index = m_history_items.Count - 1;
        }
    }
    public void DeleteHistoryItem(object item)
    {
        if (m_history_items != null)
        {
            m_history_items.Remove(item);
            m_history_item_index = m_history_items.Count - 1;
        }
    }
    public void DeleteCurrentHistoryItem()
    {
        if (m_history_items != null)
        {
            if ((m_history_item_index >= 0) && (m_history_item_index < m_history_items.Count))
            {
                object item = m_history_items[m_history_item_index];
                m_history_items.Remove(item);
                m_history_item_index = m_history_items.Count - 1;
            }

            if (m_history_items.Count == 0) // all items deleted
            {
                m_history_item_index = -1;
            }
            else // there are still some item(s)
            {
                // if index becomes outside list, move back into list
                if (m_history_item_index == m_history_items.Count)
                {
                    m_history_item_index = m_history_items.Count - 1;
                }
            }
        }
    }
    public void ClearHistoryItems()
    {
        if (m_history_items != null)
        {
            m_history_items.Clear();
            m_history_item_index = -1;
        }
    }
    public object GotoPreviousHistoryItem()
    {
        object result = null;
        if (m_history_items != null)
        {
            if ((m_history_item_index > 0) && (m_history_item_index < m_history_items.Count))
            {
                m_history_item_index--;
                result = m_history_items[m_history_item_index];
            }
        }
        return result;
    }
    public object GotoNextHistoryItem()
    {
        object result = null;
        if (m_history_items != null)
        {
            if ((m_history_item_index >= -1) && (m_history_item_index < m_history_items.Count - 1))
            {
                m_history_item_index++;
                result = m_history_items[m_history_item_index];
            }
        }
        return result;
    }
    public void SaveHistoryItems()
    {
        if (m_history_items != null)
        {
            string user_history_directory = m_history_directory + "/" + m_username;
            if (!Directory.Exists(user_history_directory))
            {
                Directory.CreateDirectory(user_history_directory);
            }

            string filename = user_history_directory + "/" + "History" + ".txt";
            try
            {
                using (StreamWriter writer = new StreamWriter(filename, false, Encoding.Unicode))
                {
                    StringBuilder str = new StringBuilder();

                    foreach (object history_item in m_history_items)
                    {
                        if (history_item is SelectionHistoryItem)
                        {
                            SelectionHistoryItem item = history_item as SelectionHistoryItem;
                            if (item != null)
                            {
                                str.AppendLine("BrowseHistoryItem");
                                if (item.Indexes.Count > 0)
                                {
                                    str.AppendLine(item.Scope.ToString());
                                    foreach (int index in item.Indexes)
                                    {
                                        str.Append(index.ToString() + ",");
                                    }
                                    str.Remove(str.Length - 1, 1);
                                    str.AppendLine();
                                    str.AppendLine(END_OF_HISTORY_ITME_MARKER);
                                }
                            }
                        }
                        else if (history_item is FindHistoryItem)
                        {
                            FindHistoryItem item = history_item as FindHistoryItem;
                            if (item != null)
                            {
                                str.AppendLine("FindHistoryItem");
                                str.AppendLine("Text" + "\t" + item.Text);
                                str.AppendLine("Header" + "\t" + item.Header);
                                str.AppendLine("Language" + "\t" + item.LanguageType);
                                str.AppendLine("Translation" + "\t" + item.Translation);

                                if ((item.Phrases != null) && (item.Phrases.Count > 0))
                                {
                                    foreach (Phrase phrase in item.Phrases)
                                    {
                                        str.AppendLine(phrase.Verse.Number.ToString() + "," + phrase.Text + "," + phrase.Position.ToString());
                                    }
                                }
                                else
                                {
                                    foreach (Verse verse in item.Verses)
                                    {
                                        str.AppendLine(verse.Number.ToString());
                                    }
                                }
                                str.AppendLine(END_OF_HISTORY_ITME_MARKER);
                            }
                        }
                    }
                    writer.Write(str.ToString());
                }
            }
            catch
            {
                // silence IO error in case running from read-only media (CD/DVD)
            }
        }
    }
    public void LoadHistoryItems()
    {
        string filename = m_history_directory + "/" + m_username + "/" + "History" + ".txt";
        if (File.Exists(filename))
        {
            using (StreamReader reader = File.OpenText(filename))
            {
                try
                {
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine();
                        string[] parts = null;

                        if (line == "")
                        {
                            continue;
                        }
                        else if (line == "BrowseHistoryItem")
                        {
                            line = reader.ReadLine();
                            SelectionScope scope = (SelectionScope)Enum.Parse(typeof(SelectionScope), line);
                            List<int> indexes = new List<int>();

                            line = reader.ReadLine();
                            parts = line.Split(',');
                            foreach (string part in parts)
                            {
                                try
                                {
                                    int index = int.Parse(part);
                                    indexes.Add(index);
                                }
                                catch
                                {
                                    continue;
                                }
                            }

                            SelectionHistoryItem item = new SelectionHistoryItem(book, scope, indexes);
                            AddHistoryItem(item);
                        }
                        else if (line == "FindHistoryItem")
                        {
                            FindHistoryItem item = new FindHistoryItem();

                            line = reader.ReadLine();
                            parts = line.Split('\t');
                            if ((parts.Length == 2) && (parts[0].Trim() == "Text"))
                            {
                                item.Text = parts[1].Trim();

                                line = reader.ReadLine();
                                parts = line.Split('\t');
                                if ((parts.Length == 2) && (parts[0].Trim() == "Header"))
                                {
                                    item.Header = parts[1].Trim();

                                    line = reader.ReadLine();
                                    parts = line.Split('\t');
                                    if ((parts.Length == 2) && (parts[0].Trim() == "Language"))
                                    {
                                        item.LanguageType = (FindByTextLanguageType)Enum.Parse(typeof(FindByTextLanguageType), parts[1].Trim());

                                        line = reader.ReadLine();
                                        parts = line.Split('\t');
                                        if ((parts.Length == 2) && (parts[0].Trim() == "Translation"))
                                        {
                                            item.Translation = parts[1].Trim();

                                            // CSV: Phrase.Verse.Number, Phrase.Text, Phrase.Position
                                            while (true)
                                            {
                                                line = reader.ReadLine();
                                                if (line == END_OF_HISTORY_ITME_MARKER)
                                                {
                                                    break;
                                                }
                                                parts = line.Split(',');
                                                if (parts.Length == 1) // verse.Number
                                                {
                                                    int verse_index = int.Parse(parts[0].Trim()) - 1;
                                                    if ((verse_index >= 0) && (verse_index < book.Verses.Count))
                                                    {
                                                        Verse verse = book.Verses[verse_index];
                                                        if (!item.Verses.Contains(verse))
                                                        {
                                                            item.Verses.Add(verse);
                                                        }
                                                    }
                                                }
                                                else if (parts.Length == 3) // phrase.Verse.Number,phrase.Text,phrase.Position
                                                {
                                                    int verse_index = int.Parse(parts[0].Trim()) - 1;
                                                    if ((verse_index >= 0) && (verse_index < book.Verses.Count))
                                                    {
                                                        Verse verse = book.Verses[verse_index];
                                                        if (!item.Verses.Contains(verse))
                                                        {
                                                            item.Verses.Add(verse);
                                                        }

                                                        string phrase_text = parts[1].Trim();
                                                        if (phrase_text.Length > 0)
                                                        {
                                                            int phrase_position = int.Parse(parts[2].Trim());
                                                            Phrase phrase = new Phrase(verse, phrase_text, phrase_position);
                                                            item.Phrases.Add(phrase);
                                                        }
                                                    }
                                                }
                                            } // while
                                        }
                                    }
                                }
                            }
                            AddHistoryItem(item);
                        }
                    } // while
                }
                catch
                {
                    throw new Exception("Invalid " + filename + " format.");
                }
            }
        }
    }
    private string END_OF_HISTORY_ITME_MARKER = "-------------------";

    private List<Bookmark> m_bookmarks = new List<Bookmark>();
    public List<Bookmark> Bookmarks
    {
        get { return m_bookmarks; }
    }
    private Bookmark m_current_bookmark;
    public Bookmark CurrentBookmark
    {
        get { return m_current_bookmark; }
    }
    private int m_current_bookmark_index = -1;
    public int CurrentBookmarkIndex
    {
        get
        {
            if (m_bookmarks != null)
            {
                for (int i = 0; i < m_bookmarks.Count; i++)
                {
                    if (m_bookmarks[i] == m_current_bookmark)
                    {
                        if (i == m_current_bookmark_index)
                        {
                            return i;
                        }
                        else
                        {
                            throw new Exception("current=" + m_current_bookmark_index + "\t\tbookmark_index=" + i);
                        }
                    }
                }
            }
            return -1;
        }
    }
    public int GetBookmarkIndex(Bookmark bookmark)
    {
        if (m_bookmarks != null)
        {
            for (int i = 0; i < m_bookmarks.Count; i++)
            {
                if (m_bookmarks[i] == bookmark)
                {
                    return i;
                }
            }
        }
        return -1;
    }
    public Bookmark GetBookmark(Selection selection)
    {
        if (selection != null)
        {
            // selection is mutable so we cannot use ==
            //foreach (Bookmark bookmark in m_bookmarks)
            //{
            //    if (bookmark.Selection == selection)
            //    {
            //        return bookmark;
            //    }
            //}
            return GetBookmark(selection.Scope, selection.Indexes);
        }
        return null;
    }
    public Bookmark GetBookmark(SelectionScope scope, List<int> indexes)
    {
        if (m_bookmarks != null)
        {
            foreach (Bookmark bookmark in m_bookmarks)
            {
                if (bookmark.Selection.Scope == scope)
                {
                    if (bookmark.Selection.Indexes.Count == indexes.Count)
                    {
                        int matching_indexes = 0;
                        for (int i = 0; i < bookmark.Selection.Indexes.Count; i++)
                        {
                            if (bookmark.Selection.Indexes[i] == indexes[i])
                            {
                                matching_indexes++;
                            }
                        }
                        if (indexes.Count == matching_indexes)
                        {
                            return bookmark;
                        }
                    }
                }
            }
        }
        return null;
    }
    public Bookmark GotoBookmark(Selection selection)
    {
        Bookmark bookmark = null;
        if (selection != null)
        {
            bookmark = GetBookmark(selection.Scope, selection.Indexes);
            if (bookmark != null)
            {
                m_current_bookmark = bookmark;
                m_current_bookmark_index = GetBookmarkIndex(bookmark);
            }
        }
        return bookmark;
    }
    public Bookmark GotoBookmark(SelectionScope scope, List<int> indexes)
    {
        Bookmark bookmark = GetBookmark(scope, indexes);
        if (bookmark != null)
        {
            m_current_bookmark = bookmark;
            m_current_bookmark_index = GetBookmarkIndex(bookmark);
        }
        return bookmark;
    }
    public Bookmark GotoNextBookmark()
    {
        if (m_bookmarks != null)
        {
            if (m_bookmarks.Count > 0)
            {
                if (m_current_bookmark_index < m_bookmarks.Count - 1)
                {
                    m_current_bookmark_index++;
                    m_current_bookmark = m_bookmarks[m_current_bookmark_index];
                }
            }
        }
        return m_current_bookmark;
    }
    public Bookmark GotoPreviousBookmark()
    {
        if (m_bookmarks != null)
        {
            if (m_bookmarks.Count > 0)
            {
                if (m_current_bookmark_index > 0)
                {
                    m_current_bookmark_index--;
                    m_current_bookmark = m_bookmarks[m_current_bookmark_index];
                }
            }
        }
        return m_current_bookmark;
    }
    public Bookmark AddOverwriteBookmark(Selection selection, string note)
    {
        Bookmark bookmark = GetBookmark(selection.Scope, selection.Indexes);
        if (bookmark != null) // overwrite existing bookmark
        {
            bookmark.Note = note;
            bookmark.LastModifiedTime = DateTime.Now;
            m_current_bookmark = bookmark;
        }
        else // create a new bookmark
        {
            bookmark = new Bookmark(selection, note);
            m_bookmarks.Insert(m_current_bookmark_index + 1, bookmark);
            m_current_bookmark_index++;
            m_current_bookmark = m_bookmarks[m_current_bookmark_index];
        }
        return m_current_bookmark;
    }
    public void DeleteCurrentBookmark()
    {
        Bookmark current_bookmark = CurrentBookmark;
        if (current_bookmark != null)
        {
            if (m_bookmarks != null)
            {
                m_bookmarks.Remove(current_bookmark);
                if (m_bookmarks.Count == 0) // no bookmark to display
                {
                    m_current_bookmark_index = -1;
                    m_current_bookmark = null;
                }
                else // there are bookmarks still
                {
                    // if index becomes outside list, move back into list
                    if (m_current_bookmark_index == m_bookmarks.Count)
                    {
                        m_current_bookmark_index = m_bookmarks.Count - 1;
                    }
                    m_current_bookmark = m_bookmarks[m_current_bookmark_index];
                }
            }
        }
    }
    public void ClearBookmarks()
    {
        if (m_bookmarks != null)
        {
            m_bookmarks.Clear();
            m_current_bookmark_index = -1;
            m_current_bookmark = null;
        }
    }
    public void SaveBookmarks()
    {
        if (book != null)
        {
            string directory = "Bookmarks";
            if (!Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
            }

            string filename = directory + "/" + book.Title + ".txt";
            try
            {
                using (StreamWriter writer = new StreamWriter(filename, false, Encoding.Unicode))
                {
                    if (m_bookmarks != null)
                    {
                        foreach (Bookmark bookmark in m_bookmarks)
                        {
                            if (bookmark.Note.Length > 0)
                            {
                                string scope_str = bookmark.Selection.Scope.ToString();

                                StringBuilder str = new StringBuilder();
                                for (int i = 0; i < bookmark.Selection.Indexes.Count; i++)
                                {
                                    str.Append((bookmark.Selection.Indexes[i] + 1).ToString() + "+");
                                }
                                if (str.Length > 0)
                                {
                                    str.Remove(str.Length - 1, 1);
                                }

                                string created_time = bookmark.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss");
                                string last_modified_time = bookmark.LastModifiedTime.ToString("yyyy-MM-dd HH:mm:ss");
                                string note = bookmark.Note;

                                string line = scope_str + "," + str.ToString() + "," + created_time + "," + last_modified_time + "," + note;
                                writer.WriteLine(line);
                            }
                        }
                    }
                }
            }
            catch
            {
                // silence IO error in case running from read-only media (CD/DVD)
            }
        }
    }
    public void LoadBookmarks()
    {
        if (book != null)
        {
            string directory = "Bookmarks";
            string filename = directory + "/" + book.Title + ".txt";
            if (File.Exists(filename))
            {
                using (StreamReader reader = File.OpenText(filename))
                {
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine();
                        string[] parts = line.Split(',');
                        if (parts.Length == 5)
                        {
                            try
                            {
                                SelectionScope scope = (SelectionScope)Enum.Parse(typeof(SelectionScope), parts[0]);

                                string part = parts[1].Trim();
                                string[] sub_parts = part.Split('+');
                                List<int> indexes = new List<int>();
                                foreach (string sub_part in sub_parts)
                                {
                                    indexes.Add(int.Parse(sub_part.Trim()) - 1);
                                }
                                Selection selection = new Selection(book, scope, indexes);

                                DateTime created_time = DateTime.ParseExact(parts[2], "yyyy-MM-dd HH:mm:ss", null);
                                DateTime last_modified_time = DateTime.ParseExact(parts[3], "yyyy-MM-dd HH:mm:ss", null);
                                string note = parts[4];

                                LoadBookmark(selection, note, created_time, last_modified_time);
                            }
                            catch
                            {
                                throw new Exception("Invalid data format in " + filename);
                            }
                        }
                    }
                }
            }
        }
    }
    public void LoadBookmark(Selection selection, string note, DateTime created_time, DateTime last_modified_time)
    {
        if (m_bookmarks != null)
        {
            Bookmark bookmark = AddOverwriteBookmark(selection, note);
            if (bookmark != null)
            {
                bookmark.CreatedTime = created_time;
                bookmark.LastModifiedTime = last_modified_time;
            }
        }
    }
}
